/********************************************************************
* RedundancyEliminateFilter.cpp: Impletement class RedundancyEliminateFilter.
*
* Description:
*   The main function of this cpp file is to eliminate redundancy get/put vinsts
*
* History:
*   Create: CrossBit Group, vivy. 2008-3-31 .
*   
*   Last Modified:
*       gxf. 2008-11-15
*/

#include "crossbit/VFilter.h"
#include "crossbit/VInst.h"
#include "crossbit/VBlock.h"
#include "crossbit/GLOBAL.h"

using namespace crossbit;

/*
* Notice: this optimization must happen after VInst being converted into
*         SSA form.
*/

XTUint32 RedundancyEliminateFilter::run(VBlock *vb)
{
    VBlock::vb_iterator Itr = vb->begin(); 
    VBlock::vb_iterator End = vb->end(); 

    // Sreg -> Vreg mapping table
    XTRegNum state_map[GLOBAL::MAX_VREG]        = {0};
    // VREG -> VREG' renaming table
    XTRegNum rename_map[GLOBAL::MAX_VREG * 2]   = {0}; 
    // SREG -> (VInst *) pending put inst table
    VInst *pend_map[GLOBAL::MAX_VREG]           = {NULL};     

    while(Itr != End){
        XTRegNum sreg;
        XTRegNum vreg;

        VInst *inst = *Itr;
        switch(inst -> getOpcode())
        {
            case OP_GET: 
                sreg = inst->getOperand(1).reg;  //sreg for source reg 
                vreg = inst->getOperand(2).reg;  //vreg for v reg 

                if(0 == state_map[sreg]){
                    state_map[sreg] = vreg;   //GET: sreg-->vreg
                    Itr++;
                }
                else{ 
                    rename_map[vreg] = state_map[sreg];   //Add rename pair(vreg, vreg')
                    VInst *inst = *Itr;
                    Itr = vb->erase(Itr);  
                    delete inst;      //Redundancy GET, eliminate it!
                }
                break;
        
            //if vinst is PUT, put it into pending map 
            case OP_PUT:
                sreg = inst->getOperand(2).reg;
                vreg = inst->getOperand(1).reg;

                state_map[sreg] = vreg;     //Map sreg to vreg

                if(pend_map[sreg] != NULL){
                   pend_map[sreg] = NULL;   //clear pend_map[sreg]
                }
                pend_map[sreg] = *Itr;        //Add Inst to pend_map
                Itr = vb -> erase(Itr);       //Eliminate redundancy PUT vinst
                // Notice: Itr has advanced to next
                break;

            default: 
                XTInt32 n = inst->getOperandNum(); 

                //Before jump vinst, insert PUT vinst to VBlock 
                if(inst->getOpkind() == OPK_CONTROLTRANSFER || 
                   inst->getOpcode() == OP_SYSCALL)
                { 
                    for(XTInt32 pmi = 0; pmi < 100; pmi++){ 
                        if(pend_map[pmi] != NULL){ 
                            vb->insert(Itr, pend_map[pmi]); 
                            pend_map[pmi] = NULL;
                        }
                    }
                }
                else{ 
                    // rename all the references that are previously "mapped" 
                    for (XTInt32 i = 1; i <= n; i++){ 
                        Operand oprnd = inst->getOperand(i); 
                        if(oprnd.type == Operand::VREG_USE && 
                           rename_map[oprnd.reg] != 0)
                        { 
                            oprnd.reg = rename_map[oprnd.reg];   //Get it from rename map
                            inst->setOperand(i, oprnd);          //Update current inst
                        }
                    }   
                }
                Itr++;
        }// Switch 
    }// while
    
    /*
    //Push back pending PUTs
   for(XTInt32 pmi = 0; pmi < 100; pmi++)
   {
	   if(pend_map[pmi] != NULL)
	   {
		        vb->push_back(pend_map[pmi]);
		        pend_map[pmi] = NULL;
		}
	}
	*/
	return (XTUint32)1;
}
